﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Reflection;
using System.Collections;
using System.Text.RegularExpressions;
using System.Web.UI;

namespace TA.Turbo
{
	public static class DictionaryUtils
	{
		#region DICT => OBJECT
		public static T FromDict<T>(this Dictionary<string, object> dThis)
		{
			return (T)dThis.Get(typeof(T));
		}
		#region INTERNAL
		static object Get(this Dictionary<string, object> dThis, Type type)
		{
			object t = InvokeCtor(type);
			PropertyInfo[] props = t.GetType().GetProperties();

			foreach (PropertyInfo property in props)
			{
				if (!dThis.ContainsKey(property.Name)) continue;

				object source = dThis[property.Name];
				object value = null;
				if (source is Dictionary<string, object>)
				{
					value = ((Dictionary<string, object>)source).Get(property.PropertyType);
				}
				else
				{
					try
					{
						value = Convert.ChangeType(source, property.PropertyType);
					}
					catch
					{
					}
				}
				property.SetValue(t, value, null);
			}
			return t;
		}
		private static bool IsAssignable(Type to, Type from)
		{
			if (to.Equals(from)) return true;
			if (GetNumericLevel(to) >= GetNumericLevel(from) && GetNumericLevel(from) != -1)
				return true;
			return false;
		}
		private static int GetNumericLevel(Type t)
		{
			if (t.Equals(typeof(byte)) || t.Equals(typeof(sbyte))) return 1;
			if (t.Equals(typeof(short)) || t.Equals(typeof(ushort))) return 2;
			if (t.Equals(typeof(int)) || t.Equals(typeof(uint))) return 3;
			if (t.Equals(typeof(long)) || t.Equals(typeof(ulong))) return 4;
			if (t.Equals(typeof(float)) || t.Equals(typeof(double))) return 11;
			if (t.Equals(typeof(decimal))) return 12;

			return -1;
		}
		private static object InvokeCtor(Type type)
		{
			ConstructorInfo ctor = GetCtorWithMinimumParameters(type);

			ArrayList parameters = new ArrayList();
			foreach (ParameterInfo param in ctor.GetParameters())
				parameters.Add(GetDefaultValue(param.ParameterType));
			return ctor.Invoke(parameters.ToArray());
		}

		private static object GetDefaultValue(Type type)
		{
			if (type.IsClass)
				return null;
			if (GetNumericLevel(type) != -1)
				return 0;
			if (type.Equals(typeof(bool))) return false;
			throw new ArgumentException(string.Format("Can't determine default value for type '{0}'.", type.Name));
		}
		private static ConstructorInfo GetCtorWithMinimumParameters(Type type)
		{
			ConstructorInfo[] ctors = type.GetConstructors();
			int minNumParams = int.MaxValue;
			ConstructorInfo ctorWithMinParams = null;
			//ParameterInfo[] minSetParams = null;
			foreach (ConstructorInfo ctor in ctors)
			{
				ParameterInfo[] ctorParams = ctor.GetParameters();
				if (ctorParams.Length == 0) return ctor;
				if (ctorParams.Length < minNumParams)
				{
					minNumParams = ctorParams.Length;
					//minSetParams = ctorParams;
					ctorWithMinParams = ctor;
				}
			}
			return ctorWithMinParams;
		}
		#endregion
		#endregion
		#region OBJECT => DICT
		public static Dictionary<string, object> ToDict(this object oThis, IEnumerable<string> dataFields)
		{
			Dictionary<string, object> dict = new Dictionary<string, object>();
			foreach (string oneDataField in dataFields)
			{
				Match mFieldPath = rgxBreakFieldPath.Match(oneDataField);
				Dictionary<string, object> cur = dict;
				CaptureCollection clFieldPath = mFieldPath.Groups["Field"].Captures;
				for (int iMatch = 0; iMatch < clFieldPath.Count - 1; ++iMatch)
				{
					string match = clFieldPath[iMatch].Value;
					if (!cur.ContainsKey(match) || !(cur[match] is Dictionary<string, object>))
						cur[match] = new Dictionary<string, object>();
					cur = cur[match] as Dictionary<string, object>;
				}
				cur[clFieldPath[clFieldPath.Count - 1].Value] = DataBinder.Eval(oThis, oneDataField);
			}
			return dict;
		}
		static Regex rgxBreakFieldPath = new Regex(@"(?<Field>[\w_]+)(\.(?<Field>[\w_]+))*");
		#endregion
	}
}
